package org.totoro.amap.service.impl;

import cn.hutool.core.util.URLUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.totoro.amap.config.Key;
import org.totoro.amap.repo.PoiEntityRepo;
import org.totoro.amap.repo.entity.PoiEntityEntity;
import org.totoro.amap.response.PoiSearchResponse;
import org.totoro.amap.service.PoiCityInfoPolylineService;
import org.totoro.amap.service.PoiService;
import org.totoro.amap.service.bo.PoiCityInfoPolylineBo;
import org.totoro.amap.service.bo.PoiInfoBo;

import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
 * @author daocr
 * @date 2019/11/17
 */
@Service
@Slf4j
public class PoiServiceImpl implements PoiService {

    @Autowired
    private PoiCityInfoPolylineService poiCityInfoPolylineService;

    @Autowired
    private PoiEntityRepo poiEntityRepo;


    private GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null);

    @Override
    public void searchAndSave(String adcCode, String poiType) throws ParseException {

        PoiCityInfoPolylineBo poiCityInfoPolylineBo = poiCityInfoPolylineService.queryByAdcCode(adcCode);

        String[] split = poiCityInfoPolylineBo.getGeo().split("\\|");

        for (String geo : split) {

            String replacePolygon = geo.replace(",", " ").replace(";", ",");

            String tpl = "POLYGON((%s))";

            WKTReader reader = new WKTReader(geometryFactory);

            Polygon polygon = (Polygon) reader.read(String.format(tpl, replacePolygon));

            List<Polygon> analysis = analysis(polygon, poiType, 1);

            for (Polygon analysis1 : analysis) {

                PoiSearchResponse poiSearchResponse = getPoiSearchResponse(poiType, toGaodePolygon(analysis1), 1);

                Integer totalSize = new Double(Math.ceil(poiSearchResponse.getCount().doubleValue() / 20)).intValue();

                for (Integer index = 1; index <= totalSize; index++) {

                    PoiSearchResponse poiSearchResponse1 = getPoiSearchResponse(poiType, toGaodePolygon(analysis1), index);

                    List<PoiSearchResponse.PoisVO> pois = poiSearchResponse1.getPois();

                    if (ObjectUtils.isEmpty(pois)) {

                        for (PoiSearchResponse.PoisVO poisVO : pois) {


                        }

                    }


                }

            }


        }
    }

    private PoiSearchResponse getPoiSearchResponse(String poiType, String replacePolygon, Integer pageNo) {

        String url = "restapi.amap.com/v3/place/polygon?key=" + Key.loadbalancing() + "&polygon=" + URLUtil.encode(replacePolygon, "utf-8") + "&keywords=&types=" + poiType + "&offset=20&page=" + pageNo + "&extensions=all";

        HttpResponse execute = HttpUtil.createGet(url).execute();

        return JSONUtil.toBean(execute.body(), PoiSearchResponse.class);
    }


    private List<Polygon> analysis(Polygon polygon, String poiType, Integer pageNo) throws ParseException {

        PoiSearchResponse poiSearchResponse = getPoiSearchResponse(poiType, toGaodePolygon(polygon), pageNo);

        if (poiSearchResponse.getCount() < 1000) {
            return Arrays.asList(polygon);
        }

        int limit = 3;

        Envelope envelopeInternal = polygon.getEnvelopeInternal();

        double offsetX = envelopeInternal.getMinX();
        double offsetY = envelopeInternal.getMinY();

        double width = envelopeInternal.getWidth() / limit;

        double height = envelopeInternal.getHeight() / limit;

        ArrayList<Polygon> envelopes = new ArrayList<>();

        WKTReader reader = new WKTReader(geometryFactory);

        String tpl = "POLYGON((%s %s,%s %s,%s %s,%s %s,%s %s))";

        for (int i = 0; i < limit; i++) {

            for (int i1 = 0; i1 < limit; i1++) {

                // min x
                Double x1 = this.conv(offsetX + (width * i));
                // max x
                Double x2 = this.conv(offsetX + (width * (i + 1)));

                //min y
                Double y1 = this.conv(offsetY + (height * i1));
                //max y
                Double y2 = this.conv(offsetY + (height * (i1 + 1)));

                Polygon polygon2 = (Polygon) reader.read(String.format(tpl, x1, y2, x2, y2, x2, y1, x1, y1, x1, y2));

                List<Polygon> analysis = analysis(polygon2, poiType, pageNo);

                log.info("{}|{}", x1 + "," + y2, x2 + "," + y1);

                envelopes.addAll(analysis);

            }
        }

        return envelopes;
    }

    /**
     * 转换为 高德可搜索的文本
     *
     * @param polygon
     * @return
     */
    String toGaodePolygon(Polygon polygon) {

        Coordinate[] coordinates = polygon.getCoordinates();

        String gaode = Arrays.asList(coordinates).stream().map(e -> {
            return e.getX() + "," + e.getY();
        }).reduce((e1, e2) -> {
            return e1 + "|" + e2;
        }).get();

        return gaode;
    }


    private Double conv(Double value) {
        return BigDecimal.valueOf(value).setScale(6, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    @Override
    public PoiInfoBo save(PoiSearchResponse poiInfoBo) {


        PoiEntityEntity poiEntityEntity = new PoiEntityEntity();

        poiEntityRepo.save(poiEntityEntity);

        return null;
    }

    /**
     * 转换
     *
     * @param poisVO
     * @return
     */
    PoiEntityEntity conv(PoiSearchResponse.PoisVO poisVO) {

        PoiEntityEntity poiEntityEntity = new PoiEntityEntity();

        poiEntityEntity.setGaodeId(poisVO.getId());
        poiEntityEntity.setGaodeParent(Optional.ofNullable(poisVO.getParent()).map( JSONUtil::toJsonPrettyStr).orElse(null));
        poiEntityEntity.setPoiName(poisVO.getName());
        poiEntityEntity.setTypeCode(poisVO.getTypecode());
        poiEntityEntity.setTypeName(poisVO.getType());
        poiEntityEntity.setBizType(Optional.ofNullable(poisVO.getBiz_type()).map( JSONUtil::toJsonPrettyStr).orElse(null));
        poiEntityEntity.setAddress(poisVO.getAddress());
        poiEntityEntity.setLocation(poisVO.getLocation());
        poiEntityEntity.setTel(Optional.ofNullable(poisVO.getTel()).map( JSONUtil::toJsonPrettyStr).orElse(null));
        poiEntityEntity.setProvinceCode(poisVO.getPcode());
        poiEntityEntity.setProvinceCode(poisVO.getPname());
        poiEntityEntity.setCityCode(poisVO.getCitycode());
        poiEntityEntity.setCityName(poisVO.getCityname());
        poiEntityEntity.setDistrictCode(poisVO.getAdcode());
        poiEntityEntity.setDistrictName(poisVO.getAdname());
        poiEntityEntity.setEntrLocation(poisVO.getEntr_location());
        poiEntityEntity.setAlias(Optional.ofNullable(poisVO.getAlias()).map( JSONUtil::toJsonPrettyStr).orElse(null));
        poiEntityEntity.setBusinessArea(poisVO.getBusiness_area());
        poiEntityEntity.setParkingType(poisVO.getParking_type());
        poiEntityEntity.setTag(Optional.ofNullable(poisVO.getTag()).map( JSONUtil::toJsonPrettyStr).orElse(null));

        PoiSearchResponse.PoisVO.BizExtVO bizExt = poisVO.getBiz_ext();

        if(!ObjectUtils.isEmpty(bizExt)){
            poiEntityEntity.setBizRating(Optional.ofNullable(poisVO.getBiz_ext().getRating()).map( JSONUtil::toJsonPrettyStr).orElse(null));
            poiEntityEntity.setBizCost(Optional.ofNullable(poisVO.getBiz_ext().getCost()).map( JSONUtil::toJsonPrettyStr).orElse(null));
        }


        return poiEntityEntity;

    }


}

